﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ServiceTable.Tool;
using ServiceTable.Tool.JSON;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace RimeLoRaTool.LoraServer
{
    public class LoraServerIOPiper : ILoraServerIOPiper
    {
        private LoraServerContext _ApiRunConfig;

        public LoraServerContext ApiRunConfig
        {
            get
            {
                return _ApiRunConfig;
            }
        }

        public LoraServerIOPiper(LoraServerContext apiRunConfig)
        {
            _ApiRunConfig = apiRunConfig;
        }

        #region 网关

        public int GetGatewayCnt(int organizationID)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = 0;
            ps["offset"] = 0;
            ps["organizationID"] = organizationID;
            string result = _Get(ApiOffsetPath.API_GATEWAYS, ps);
            JObject robj = JObject.Parse(result);
            return robj.Value<int>("totalCount"); 
        }

        public List<GatewayInfo> GetGatewayList(int limit, int offset, int organizationID)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = limit;
            ps["offset"] = offset;
            ps["organizationID"] = organizationID;
            string result = _Get(ApiOffsetPath.API_GATEWAYS, ps);
            JObject robj = JObject.Parse(result);
            JArray list = robj.Value<JArray>("result");
            List<GatewayInfo> gwlist = new List<GatewayInfo>();
            foreach (JObject gw in list)
            {
                GatewayInfo gwi = new GatewayInfo();
                gwi.createdAt = gw.Value<String>("createdAt");
                gwi.description = gw.Value<String>("description");
                gwi.mac = gw.Value<String>("id");
                gwi.name = gw.Value<String>("name");
                gwi.networkServerID = gw.Value<String>("networkServerID");
                gwi.organizationID = gw.Value<String>("organizationID");
                gwi.updatedAt = gw.Value<String>("updatedAt");
                gwi.lastSeenAt = gw.Value<String>("lastSeenAt");
                gwlist.Add(gwi);
            }
            return gwlist;
        }

        /// <summary>
        /// 最小添加网关
        /// </summary>
        /// <param name="mac"></param>
        /// <param name="name"></param>
        /// <param name="networkServerID"></param>
        /// <param name="organizationID"></param>
        /// <param name="desc"></param>
        /// <returns></returns>
        public ValidateResult AddGateway(string mac, string name, string description, string gatewayProfileID, int networkServerID, int organizationID)
        {
            JObject body = new JObject();
            JObject gateway = new JObject();
            gateway["id"] = mac;
            gateway["name"] = name;
            gateway["description"] = description;
            gateway["gatewayProfileID"] = gatewayProfileID;
            gateway["networkServerID"] = networkServerID;
            gateway["organizationID"] = organizationID; 

            JObject location = new JObject();
            location["latitude"] = 0;
            location["longitude"] = 0;
            location["altitude"] = 0;
            location["source"] = "UNKNOWN";
            location["accuracy"] = 0;

            gateway["location"] = location;
            body["gateway"] = gateway;

            String result = _Post(ApiOffsetPath.API_GATEWAYS, new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, "success");
        }

        #endregion

        /// <summary>
        /// 删除设备
        /// </summary>
        /// <param name="devEUI"></param>
        /// <returns></returns>
        public virtual ValidateResult DeleteDevice(string devEUI)
        {
            String result = _Delete(ApiOffsetPath.API_DEVICES + String.Format("/{0}", devEUI), new Hashtable());
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, "success");
        } 

        #region pipe url function

        protected String _Get(string offsetPath, Hashtable queryParams)
        {
            String apiUrl = _ApiRunConfig.ApiBaseUrl + offsetPath;
            System.Net.ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
            System.Net.WebClient webClientObj = new WebClientEx(2000);
            webClientObj.Headers.Add("Accept", "application/json");
            webClientObj.Headers.Add("Grpc-Metadata-Authorization", _ApiRunConfig.Token);

            String queryString = "";
            foreach (var k in queryParams.Keys)
            {
                if (!String.IsNullOrEmpty(queryString))
                {
                    queryString += "&";
                }
                queryString += String.Format("{0}={1}", k, queryParams[k]);
            }
            if (!String.IsNullOrEmpty(queryString))
            {
                apiUrl += "?" + queryString;
            }

            webClientObj.Encoding = Encoding.UTF8;
            try
            {
                String result = webClientObj.DownloadString(apiUrl);
                return result;
            }
            catch (WebException we)
            {
                if (we.Response != null)
                {
                    return new StreamReader(we.Response.GetResponseStream()).ReadToEnd();
                }
                else
                {
                    JObject weresult = new JObject();
                    weresult["error"] = we.Message;
                    return weresult.ToString(Newtonsoft.Json.Formatting.None);
                }
            }
            catch (Exception ce)
            {
                JObject ceresult = new JObject();
                ceresult["error"] = ce.Message;
                return ceresult.ToString(Newtonsoft.Json.Formatting.None);
            }
        }

        protected String _Post(string offsetPath, Hashtable queryParams, JObject body)
        {
            return _PostPut(offsetPath, queryParams, body, "POST");
        }

        protected String _Put(string offsetPath, Hashtable queryParams, JObject body)
        {
            return _PostPut(offsetPath, queryParams, body, "PUT");
        }

        protected String _PostPut(string offsetPath, Hashtable queryParams, JObject body, String method)
        {
            String apiUrl = _ApiRunConfig.ApiBaseUrl + offsetPath;
            System.Net.ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
            System.Net.WebClient webClientObj = new WebClientEx(2000); 
            webClientObj.Headers.Add("Accept", "application/json");
            webClientObj.Headers.Add("Grpc-Metadata-Authorization", _ApiRunConfig.Token);

            String queryString = "";
            foreach (var k in queryParams.Keys)
            {
                if (!String.IsNullOrEmpty(queryString))
                {
                    queryString += "&";
                }
                queryString += String.Format("{0}={1}", k, queryParams[k]);
            }
            if (!String.IsNullOrEmpty(queryString))
            {
                apiUrl += "?" + queryString;
            }

            webClientObj.Encoding = Encoding.UTF8;

            try
            {
                String result = webClientObj.UploadString(apiUrl, method, body.ToString(Newtonsoft.Json.Formatting.None));
                return result;
            }
            catch (WebException we)
            {
                if (we.Response != null)
                {
                    return new StreamReader(we.Response.GetResponseStream()).ReadToEnd();
                }
                else
                {
                    JObject weresult = new JObject();
                    weresult["error"] = we.Message;
                    return weresult.ToString(Newtonsoft.Json.Formatting.None);
                }
            }
            catch (Exception ce)
            {
                JObject ceresult = new JObject();
                ceresult["error"] = ce.Message;
                return ceresult.ToString(Newtonsoft.Json.Formatting.None);
            }
        }

        protected String _Delete(string offsetPath, Hashtable queryParams)
        {
            String apiUrl = _ApiRunConfig.ApiBaseUrl + offsetPath;
            System.Net.ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
            System.Net.WebClient webClientObj = new WebClientEx(2000);
            webClientObj.Headers.Add("Accept", "application/json");
            webClientObj.Headers.Add("Grpc-Metadata-Authorization", _ApiRunConfig.Token);

            String queryString = "";
            foreach (var k in queryParams.Keys)
            {
                if (!String.IsNullOrEmpty(queryString))
                {
                    queryString += "&";
                }
                queryString += String.Format("{0}={1}", k, queryParams[k]);
            }
            if (!String.IsNullOrEmpty(queryString))
            {
                apiUrl += "?" + queryString;
            }

            webClientObj.Encoding = Encoding.UTF8;

            try
            {
                String result = webClientObj.UploadString(apiUrl, "DELETE", "");
                return result;
            }
            catch (WebException we)
            {
                if (we.Response != null)
                {
                    return new StreamReader(we.Response.GetResponseStream()).ReadToEnd();
                }
                else
                {
                    JObject weresult = new JObject();
                    weresult["error"] = we.Message;
                    return weresult.ToString(Newtonsoft.Json.Formatting.None);
                }
            }
            catch (Exception ce)
            {
                JObject ceresult = new JObject();
                ceresult["error"] = ce.Message;
                return ceresult.ToString(Newtonsoft.Json.Formatting.None);
            }
        }

        #endregion

        #region 接口实现


        /// <summary>
        /// 登录接口
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public ValidateResult Login(string username, string password)
        {
            Hashtable ps = new Hashtable();
            JObject body = new JObject();
            body["password"] = password;
            body["username"] = username;
            body["email"] = username; // 从 v3.11.0 开始使用 email，不使用 username，两者都带上，则可以兼容。
            string result = _Post(ApiOffsetPath.API_LOGIN, ps, body);

            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, jo.Value<String>("jwt"));
        }

        
        public List<OrganizationInfo> GetOrganizationList(int limit, int offset)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = limit;
            ps["offset"] = offset;
            string result = _Get(ApiOffsetPath.API_ORGANIZATIONS, ps);
            JObject robj = JObject.Parse(result);
            JArray list = robj.Value<JArray>("result");
            List<OrganizationInfo> olist = new List<OrganizationInfo>();
            foreach (JObject o in list)
            {
                OrganizationInfo gwi = (OrganizationInfo)JsonConvert.DeserializeObject(o.ToString(), typeof(OrganizationInfo));
                olist.Add(gwi);
            }
            return olist;
        }

        public List<NetworkServer> GetNetworkServerList(int limit, int offset)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = limit;
            ps["offset"] = offset;
            string result = _Get(ApiOffsetPath.API_NETWORKSERVERS, ps);
            JObject robj = JObject.Parse(result);
            JArray list = robj.Value<JArray>("result");
            List<NetworkServer> olist = new List<NetworkServer>();
            foreach (JObject o in list)
            {
                NetworkServer item = (NetworkServer)JsonConvert.DeserializeObject(o.ToString(), typeof(NetworkServer));
                olist.Add(item);
            }
            return olist;
        }

        public List<ServiceProfile> GetServiceProfileList(int limit, int offset, int organizationID)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = limit;
            ps["offset"] = offset;
            ps["organizationID"] = organizationID;
            string result = _Get(ApiOffsetPath.API_SERVICEPROFILES, ps);
            JObject robj = JObject.Parse(result);
            JArray list = robj.Value<JArray>("result");
            List<ServiceProfile> olist = new List<ServiceProfile>();
            foreach (JObject o in list)
            {
                ServiceProfile item = (ServiceProfile)JsonConvert.DeserializeObject(o.ToString(), typeof(ServiceProfile));
                olist.Add(item);
            }
            return olist;
        }

        public List<GatewayProfile> GetGatewayProfileList(int limit, int offset, int networkServerID)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = limit;
            ps["offset"] = offset;
            ps["networkServerID"] = networkServerID;
            string result = _Get(ApiOffsetPath.API_GATEWAYPROFILES, ps);
            JObject robj = JObject.Parse(result);
            JArray list = robj.Value<JArray>("result");
            List<GatewayProfile> olist = new List<GatewayProfile>();
            foreach (JObject o in list)
            {
                GatewayProfile item = (GatewayProfile)JsonConvert.DeserializeObject(o.ToString(), typeof(GatewayProfile));
                olist.Add(item);
            }
            return olist;
        }

        public List<ApplicationInfo> GetApplicationList(int limit, int offset, int organizationID)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = limit;
            ps["offset"] = offset;
            ps["organizationID"] = organizationID;
            string result = _Get(ApiOffsetPath.API_APPLICATIONS, ps);
            JObject robj = JObject.Parse(result);
            JArray list = robj.Value<JArray>("result");
            List<ApplicationInfo> olist = new List<ApplicationInfo>();
            foreach (JObject o in list)
            {
                ApplicationInfo item = (ApplicationInfo)JsonConvert.DeserializeObject(o.ToString(), typeof(ApplicationInfo));
                olist.Add(item);
            }
            return olist;
        }

        public List<DeviceProfile> GetDeviceProfileList(int limit, int offset, int organizationID)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = limit;
            ps["offset"] = offset;
            ps["organizationID"] = organizationID;
            string result = _Get(ApiOffsetPath.API_DEVICEPROFILES, ps);
            JObject robj = JObject.Parse(result);
            JArray list = robj.Value<JArray>("result");
            List<DeviceProfile> olist = new List<DeviceProfile>();
            foreach (JObject o in list)
            {
                DeviceProfile item = (DeviceProfile)JsonConvert.DeserializeObject(o.ToString(), typeof(DeviceProfile));
                olist.Add(item);
            }
            return olist;
        }

        public DeviceProfile GetDeviceProfile(string deviceProfileID)
        {
            Hashtable ps = new Hashtable(); 
            string result = _Get(ApiOffsetPath.API_DEVICEPROFILES + "/" + deviceProfileID, ps);
            JObject robj = JObject.Parse(result);
            if(robj.Property("error") != null)
            {
                throw new Exception(robj.Value<string>("error"));
            }
            JObject dp = robj.Value<JObject>("deviceProfile");
            DeviceProfile item = (DeviceProfile)JsonConvert.DeserializeObject(dp.ToString(), typeof(DeviceProfile));

            return item;
        }

        // 新增部分
        public ValidateResult AddNetworkServer(string name, string server)
        {
            JObject body = new JObject();
            JObject info = new JObject();
            info["name"] = name;
            info["server"] = server; 
            body["networkServer"] = info;
            String result = _Post(ApiOffsetPath.API_NETWORKSERVERS, new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, jo.Value<String>("id"));
        }

        public ValidateResult AddServiceProfile(string name, int networkServerID, bool addGWMetaData, int organizationID)
        {
            JObject body = new JObject();
            JObject info = new JObject();
            info["name"] = name;
            info["networkServerID"] = networkServerID;
            info["addGWMetaData"] = addGWMetaData;
            info["organizationID"] = organizationID;
            body["serviceProfile"] = info;
            String result = _Post(ApiOffsetPath.API_SERVICEPROFILES, new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, jo.Value<String>("id"));
        }
        public ValidateResult AddGatewayProfile(string name, int networkServerID, string channels)
        {
            JObject body = new JObject();
            JObject info = new JObject();
            info["name"] = name;
            info["networkServerID"] = networkServerID;
            info["channels"] = JArray.Parse(JsonConvert.SerializeObject(channels.Split(',')));
            body["gatewayProfile"] = info;
            String result = _Post(ApiOffsetPath.API_GATEWAYPROFILES, new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, jo.Value<String>("id"));
        }

        public ValidateResult AddDeviceProfile(string name, int networkServerID, bool supportsJoin, bool supportsClassC, int organizationID)
        {
            JObject body = new JObject();
            JObject info = new JObject();
            info["name"] = name;
            info["networkServerID"] = networkServerID;
            info["macVersion"] = "1.0.2";
            info["regParamsRevision"] = "A";
            info["maxEIRP"] = 17;
            info["supportsJoin"] = supportsJoin;
            info["supportsClassC"] = supportsClassC;
            info["organizationID"] = organizationID;
            info["factoryPresetFreqs"] = JArray.Parse(JsonConvert.SerializeObject(new int[] { 486300000, 486500000, 486700000, 486900000, 487100000, 487300000, 487500000, 487700000 }));
            info["rxFreq2"] = 505300000;
            body["deviceProfile"] = info;
            String result = _Post(ApiOffsetPath.API_DEVICEPROFILES, new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, jo.Value<String>("id"));
        }

        public ValidateResult AddApplication(string name, int organizationID, string serviceProfileID, string description)
        {
            JObject body = new JObject();
            JObject application = new JObject();
            application["name"] = name;
            application["organizationID"] = organizationID;
            application["serviceProfileID"] = serviceProfileID;
            application["description"] = description;
            body["application"] = application;
            String result = _Post(ApiOffsetPath.API_APPLICATIONS, new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, jo.Value<String>("id"));
        }

        public ValidateResult AddDevice(int applicationID, string name, string devEUI, string deviceProfileID, string description)
        {
            JObject body = new JObject();
            JObject device = new JObject();
            device["name"] = name;
            device["devEUI"] = devEUI;
            device["applicationID"] = applicationID;
            device["deviceProfileID"] = deviceProfileID;
            device["description"] = description;
            device["skipFCntCheck"] = true;
            body["device"] = device;
            String result = _Post(ApiOffsetPath.API_DEVICES, new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, jo.Value<String>("id"));
        }

        public ValidateResult AddDeviceKey(string devEUI, string appKey)
        {
            JObject body = new JObject();

            JObject deviceKeys = new JObject();
            deviceKeys["appKey"] = appKey;
            deviceKeys["genAppKey"] = appKey;
            deviceKeys["nwkKey"] = appKey;
            deviceKeys["devEUI"] = devEUI;
            body["deviceKeys"] = deviceKeys;
            String result = _Post(ApiOffsetPath.API_DEVICES + String.Format("/{0}/keys", devEUI), new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, "success");
        }

        public ValidateResult UpdateDeviceActivation(string devEUI, string devAddr, string appSKey, string nwkSKey)
        {
            JObject body = new JObject();
            JObject deviceActivation = new JObject();
            deviceActivation["devAddr"] = devAddr;
            deviceActivation["appSKey"] = appSKey;
            deviceActivation["nwkSEncKey"] = nwkSKey;
            deviceActivation["nFCntDown"] = 0;
            deviceActivation["fCntUp"] = 0;
            deviceActivation["devEUI"] = devEUI;

            deviceActivation["fNwkSIntKey"] = nwkSKey;
            deviceActivation["sNwkSIntKey"] = nwkSKey;

            body["deviceActivation"] = deviceActivation;

            String result = _Post(ApiOffsetPath.API_DEVICES + String.Format("/{0}/activate", devEUI), new Hashtable(), body);
            JObject jo = JObject.Parse(result);
            if (JSONHelper.HasProperty(jo, "error"))
            {
                return new ValidateResult(false, jo.Value<String>("error"));
            }
            return new ValidateResult(true, "success");
        }

        public int GetDeviceCnt(int applicationID)
        {
            Hashtable ps = new Hashtable();
            ps["limit"] = 0;
            ps["offset"] = 0;
            ps["applicationID"] = applicationID;
            string result = _Get(ApiOffsetPath.API_DEVICES, ps);
            JObject robj = JObject.Parse(result);
            return robj.Value<int>("totalCount");
        }

        #endregion

    }
}
